package ArithmeticLibrary.Sets;

import java.math.BigInteger;

import ArithmeticLibrary.Elements.ElementField;
import ArithmeticLibrary.Elements.ElementPrimeField;
import ArithmeticLibrary.Util.BigIntegerModulo;
import ByteTree.ByteTree;

/**
 * This class implements prime modulo field: Z_p.
 * 
 * @author Lea Golan
 *
 */
public class PrimeField extends Field {

	private BigIntegerModulo p; //prime order field.
	private ElementPrimeField unitElement;// holds 1
	
	/**
	 * constructs a new prime field: Z_p
	 * 
	 * @param p - a prime number
	 * @throws Exception - if p is not a prime positive number
	 */
	public PrimeField(java.math.BigInteger p) throws Exception{
		if (p.compareTo(BigInteger.ZERO) != 1) {
			throw new Exception("BuildError PrimeField: p must be a positive prime");
		}
		this.p = new BigIntegerModulo(p);	
		this.unitElement = new ElementPrimeField(this.p.getValModulo(1), this);
	}

	/**
	 * BigIntegerModulo is a class for computing operations on BigInteger modulo a prime number: p
	 * @return the field p
	 */
	public BigIntegerModulo getP(){
		return this.p;
	}
	
	/**
	 * Checks if the specified element is in the field, 
	 * meaning the element is a non-negative number and lower than the field order
	 * 
	 * @param element - an element of a field
	 * @return true iff the element belongs to this field
	 */
	@Override
	public boolean belong(ElementField element) {
		return this.equals(element.getField());
	}
	
	/**
	 * Creates a new Field element by encoding message into the field.
	 * 
	 * @param message - a byte tree of the form leaf(bytes_k(n))
	 * @return a new field element according to parameters extracted from message
	 * @throws Exception - if the byte tree is not appropriately formatted 
	 */
	public ElementPrimeField createElement(ByteTree message) throws Exception{
		try {
			return new ElementPrimeField(message, this);
		} catch (Exception e) {
			throw new Exception("error in create ElementPrimeField:\n"+e.getMessage());
		}
	}
	
	/**
	 * Compare this to obj
	 */
	@Override
	public boolean equals(Object obj) {
		if (obj == this){
            return true;
        }
        if (!(obj instanceof PrimeField)){
            return false;
        }
        
        PrimeField other = (PrimeField) obj;
        return ( (this.p.equals(other.p)) &&
        		 (this.unitElement.getElementValue().equals(other.unitElement.getElementValue()))  );
	}
	
	/**
	 * @return the prime order of the field
	 */
	public java.math.BigInteger getOrder(){
		return this.p.getPModulo();
	}

	/**
	 * @return the unit element of the Field
	 */
	public ElementPrimeField getFieldIdentityElement() {
		return unitElement;
	}
		
}
